#!/usr/bin/python3

import time

'''
一、嵌套函数的作用
1、封装：嵌套函数提供了一种封装代码的方式，使得内部函数只能被外部函数访问（如果外部函数不返回内部函数的话）。
   这有助于隐藏实现细节，只暴露必要的接口。
2、闭包：如上所述，当外部函数返回内部函数时，就创建了一个闭包。闭包可以记住并访问其外部函数的局部变量，
   即使外部函数已经执行完毕。
3、装饰器：Python的装饰器通常是通过嵌套函数实现的。装饰器允许我们在不修改原有函数代码的情况下，
   给函数添加新的功能。
4、函数工厂：嵌套函数可以用来创建具有特定行为的函数。外部函数可以接收参数，这些参数随后被用来定义内部函数的行为。
   然后，外部函数返回这个内部函数，这样我们就得到了一个“函数工厂”。

二、注意事项
1、嵌套函数可以访问外部函数的局部变量，但不能修改外部函数的局部变量（除非这些变量是可变的，如列表或字典）。
2、嵌套函数可以访问外部函数的参数。
3、如果外部函数返回了内部函数，那么内部函数将保持对外部函数局部变量的引用，这可能会导致内存使用增加，
   因为Python的垃圾回收机制不会回收这些变量，直到闭包不再被引用。
4、嵌套函数提供了一种强大的编程范式，但应谨慎使用，以避免代码过于复杂和难以理解。

三、闭包
在Python中，闭包（Closure）是一个非常重要的概念，它涉及到函数及其外部作用域（非全局作用域）的变量之间的关系。
简单来说，闭包是一个函数值，它引用了其外部作用域中的变量。即使外部函数已经执行完毕，闭包中的这些变量依然可以被访问和修改。

四、闭包通常包含两个部分：
1、函数：一个内部函数，它引用了外部函数的局部变量。
2、这些局部变量的环境：即使外部函数已经执行完毕，闭包依然可以访问这些局部变量。

五、闭包的创建
闭包通常通过嵌套函数来实现。外部函数定义了局部变量，内部函数（闭包）则引用了这些局部变量。当外部函数返回内部函数时，就创建了一个闭包。
                        
参考 ：https://blog.csdn.net/weixin_45720999/article/details/141566283
'''

# 1、位置参数和关键字参数（命名参数）结合使用
"""
1）*args参数必须在**kwargs前面，因为位置参数必须在关键词参数前面。
2）*args可以用来表示任何多个无名参数，本质上是tuple类型。
3）**kwargs可以用来表示关键字参数，本质上是dict类型
"""
def func1(*args, **kwargs):
	print("*args参数的类型：", type(args))
	print("*args参数的内容：", args)
	print("**kwargs参数的类型：", type(kwargs))
	print("**kwargs参数的内容：", kwargs)

# func1(1, 2, 'hello world', a = 4, b = 6, c = 'oo',d=[])

# 2、*args在后面，前面必须是位置参数
def func2(x, *args):
	print("x的实参是{}".format(x))
	print("可变参数的内容是{}".format(args))
	print("可变参数的类型是", type(args))

# func2(1,2 , 3, 4, 5)

# 3、*args在最前面，后面必须为关键字参数
def func3(*args, x, y):
	print("a:{}".format(x))
	print("b:{}".format(y))
	print("number:{}".format(args))
	print(type(args))

# func3(1, 2, 3, x = 4, y = 5)

#  4、*argx在中间，前面必须是位置参数，后面必须为关键字参数
def func4(x, *args, y):
	print("a:{}".format(x))
	print("b:{}".format(y))
	print("number:{}".format(args))
	print(type(args))

# func4(1,2, 3, 4, y = 5)

#  5、*args打包的实数拆成单个形参。
def func5(arg1, arg2, arg3):
	print(arg1, arg2, arg3)
args=[1, 2, 3]
# func5(*args)

# 6、**kwargs可以用来发送一个不定长度键值对的字典参数给一个函数。
def func6(**kwargs):
	for key,value in kwargs.items():
		print("{0}=={1}".format(key,value))
# func6(a=4, b=6, c='oo', d=[])

# 7、**kwargs是把字典的键值拆成单个的，依次赋值给函数的形参
def func7(arg1,arg2,arg3):
	print(arg1, arg2, arg3)

kwargs={'arg1':1, 'arg2':2, 'arg3':3}
# func7(**kwargs)

# 8、嵌套函数、闭包、装饰器和lambda匿名函数，参考 https://blog.csdn.net/weixin_45720999/article/details/141566283
'''
1）闭包
定义：是一个函数值，它引用了外部作用域中的变量，即使外部函数已经执行完毕，闭包中的这些变量依然可以被访问和修改。
创建：通过嵌套函数实现，外部函数定义了局部变量 text，内部函数（闭包）则引用该局部变量 text。当外部函数返回内部函数时，就创建了一个闭包，
     因为它引用了外部函数的局部变量 text，即使 outer_function 已经执行完毕。
'''
# 外部函数
def outer_function(text):
	def inner_function():
		print(text)
	# 返回内部函数
	return inner_function
# 创建闭包
# my_closure = outer_function("Hello, World!")
# 调用闭包
# my_closure()  # 输出: Hello, World!

'''
2）装饰器，计算函数执行时间
'''
def timer(func):
	def wrapper(*args, **kwargs):
		start_time = time.time()
		result = func(*args, **kwargs)
		end_time = time.time()
		print(f"{func.__name__} 执行了 {end_time - start_time:.6f} 秒")
		# 最后要返回test_function()函数
		return result
	# 调用timer函数，返回的是wrapper
	return wrapper

'''
@timer 等价于闭包的写法： test_function = timer(test_function)
1）调用test_function函数，因为装饰器标识@timer，实际是调用timer函数；
2）调用timer函数，因为返回的是wrapper，实际是调用wrapper函数；
3）调用wrapper函数，因为返回是func，所以实际调用的是func函数;
4）因为func的实际参数是test_function，所以实际调用的是test_function函数;
总结：
1）装饰器里面的函数可以嵌套多层，但是代码会变得复制，且容易内心泄露
2）通过装饰器，增加了函数的功能，但是没有改变原来函数的代码；
3）最后返回test_function函数，形成闭环；
'''
@timer
def test_function():
	'''一个示例函数，模拟耗时操作 '''
	time.sleep(1)
	return "完成"

# 调用被装饰的函数
result = test_function()
print(result)